#ifndef PRYN_LIBRARY_H
#define PRYN_LIBRARY_H

typedef struct PrynLibrary PrynLibrary;

#include <pryn/platform.h>

typedef PrynDList (PrynLibrary) PrynLibraryList; ///< List of libraries.

#include <pryn/result.h>
#include <pryn/state.h>
#include <pryn/string.h>
#include <pryn/tag.h>

/** @defgroup PrynLibrary PrynLibrary C API
	@{ */
	
PrynImport (PrynResult, prynLibraryCreate, (PrynLibrary **library, PrynState *state, const PrynString *filename))
	/**< @brief Create a library.

	@param state The state to store the library in.
	@param[out] library Receives the library or 0 if the library could not be created.
	@param filename The filename identifying the library. 
	@returns #PrynResultSuccess or an error code; see #PrynResult. */

PrynImport (PrynResult, prynLibraryLoad, (PrynLibrary *library))
	/**< @brief Load the library if it hasn't been loaded already. 
	
	@param library The library to load.
	@returns #PrynResultSuccess if the library has been loaded, #PrynResultDone if it was already loaded, or an error code; see #PrynResult. */

PrynImport (PrynResult, prynLibraryDestroy, (PrynLibrary *library))
	/**< @brief Destroy the library and anything it contains.

	This will unload the library and destroy any types, mediators, factories, or others that come from the library. It will not destroy components or values as those are not tracked by the library, but they will become immediately nonfunctional. Therefore this operation should be done before anything else is done with the state.

	@param library The library to destroy.
	@returns #PrynResultSuccess or an error code; see #PrynResult. */

PrynImport (PrynResult, prynLibraryInPath, (PrynLibrary *library, const PrynString *path)) ///< Returns #PrynResultTrue if the @e library's filename is within this @e path, #PrynResultFalse if it is not, or an error code; see #PrynResult. This test is basic and cannot tell the difference between multiple ways to refer to the same file.

/** @name Properties
@{ */

PrynImport (PrynResult, prynLibraryName, (PrynLibrary *library, PrynString *output)) ///< Localised name of the library or empty if @e library is 0.
PrynImport (PrynResult, prynLibraryDescription, (PrynLibrary *library, PrynString *output)) ///< Localised description of the library or empty if @e library is 0.
PrynImport (PrynResult, prynLibraryState, (PrynLibrary *library, PrynState **output)) ///< Return the state for a library or 0 if @e library is 0.
PrynImport (PrynResult, prynLibraryTags, (PrynLibrary *library, PrynTagList **output)) ///< List of tags associated with the library or 0 if @e library is 0.

PrynImport (PrynResult, prynLibrarySetName, (PrynLibrary *library, const PrynString *value)) ///< Set the name of the library, adopting the value.
PrynImport (PrynResult, prynLibrarySetDescription, (PrynLibrary *library, const PrynString *value)) ///< Set the description of the library, adopting the value.

/// Set the name of the library to a literal string.
#define prynLibrarySetNameLiteral(LIBRARY, VALUE) prynStringLiteralBlock ((VALUE), STRING, { prynLibrarySetName ((LIBRARY), &STRING); })

/// Set the description of the library to a literal string.
#define prynLibrarySetDescriptionLiteral(LIBRARY, VALUE) prynStringLiteralBlock ((VALUE), STRING, { prynLibrarySetDescription ((LIBRARY), &STRING); })

/** @} */ // (properties section)

#ifdef PrynInternalStructs
/** @brief A library linkage. Libraries offer component factories.
*/
struct PrynLibrary
{
#ifdef PrynInternal
/** @name Internal fields
	These fields are not normally visible to clients, and it is recommended not to use them to maximise binary compatibility. To make them available, define PrynInternal.
	@{ */

	PrynCommonObject pCommonObject; ///< Common fields you'd expect in an object, like tags and monitors.
	PrynCommonResource pCommonResource; ///< Common fields you'd expect in a resource, like name and description.

	PrynLibrary *pNext; ///< Next library in the linked list of libraries or 0 if this is last.
	PrynLibrary *pPrevious; ///< Previous library in the linked list of libraries or 0 if this is first.
	
	PrynState *pState; ///< State this library exists within.
	PrynString pFilename; ///< Filename for this library.
	PrynTime pModified; ///< Known modification time for the library file.
	void *pPlatform; ///< Platform-specific state for the library.

/** @} */
#endif /* PrynInternal */

#if __cplusplus

	static inline PrynLibrary *Create (PrynState *state, const PrynString &filename) { PrynLibrary *result; prynLibraryCreate (&result, state, &filename); return result; }
		/**< @brief Create a library.

		@param state The state to store the library in.
		@param filename The filename identifying the library. 
		@returns The new library. */

	inline PrynResult checkError (PrynResult result) { return prynCheckErrorBase (state (), result); } ///< If the result is below zero, throw an error.

	inline bool load () { return PrynIsTrue (prynLibraryLoad (this)); }
		/**< @brief Load the library if it hasn't been loaded already. 
		
		@returns @b true if the library was loaded, @b false if the library was already loaded. */

	inline PrynType *createType (const PrynString &id); ///< Create a new type, or retrieve an already-created type if the id matches.

	inline PrynFactory *createFactory (const PrynString &id);
		/**< @brief Create a new factory or return an existing factory.

		@param id The unique identifier of the factory.
		@returns The created factory. */

	inline bool inPath (const PrynString &path) { return checkError (prynLibraryInPath (this, &path)); } ///< Returns @e true if the library's filename is within this @e path or @e false if it is not. This test is basic and cannot tell the difference between multiple ways to refer to the same file.

/** @name Properties
@{ */
	inline PrynString name () { PrynString result; prynLibraryName (this, &result); return result; } ///< Name of the library.
	inline void name (const PrynString &value) { prynLibrarySetName (this, &value); } ///< Set the name of the library; the value is adopted.
	inline PrynString description () { PrynString result; prynLibraryDescription (this, &result); return result; } ///< Description of the library.
	inline void description (const PrynString &value) { prynLibrarySetDescription (this, &value); } ///< Set the description of the library; the value is adopted.
	inline PrynState *state () { PrynState *result; prynCheckErrorNull (prynLibraryState (this, &result)); return result; } ///< The state the library exists in.
	inline PrynTagList *tags () { PrynTagList *result; prynLibraryTags (this, &result); return result; } ///< The tags associated with the library.
/** @} */

#endif /* __cplusplus */
};
#endif /* PrynInternalStructs */

/** @} */ // (PrynLibrary group)

#endif /* PRYN_LIBRARY_H */
